package internal

import (
	"errors"
	"fmt"
	"gitee.com/haozing/wim/comet/iface"
	"github.com/gobwas/ws"
	"github.com/gobwas/ws/wsutil"
	"github.com/golang/glog"
	"io"
	"net"
	"sync"
)

const (
	// PackageLess PACKAGE_LESS shows is not a completed package.
	PackageLess = iota
	// PackageFull PACKAGE_FULL shows is a completed package.
	PackageFull
	// PackageError PACKAGE_ERROR shows is a error package.
	PackageError
)
const maxMsgBuffChan = 1024

type Connection struct {
	//当前Conn属于哪个Server
	Server iface.IServer
	//当前连接的socket TCP套接字
	Conn net.Conn
	//websocket协议
	WsOpCode ws.OpCode
	//当前连接的ID 也可以称作为SessionID，ID全局唯一
	ConnID int64
	//当前连接的关闭状态
	isClosed bool
	//协议
	Transport iface.ICodec
	//告知该链接已经退出/停止的channel
	ExitBuffChan chan bool
	//无缓冲管道，用于读、写两个goroutine之间的消息通信
	//msgChan chan []byte
	//有缓冲管道，用于读、写两个goroutine之间的消息通信
	msgBuffChan chan []byte

	//链接属性
	property map[string]interface{}
	//保护链接属性修改的锁
	propertyLock sync.RWMutex

	context interface{}
}

func (c *Connection) SetContext(i interface{}) {
	c.context = i
}

func (c *Connection) Context() interface{} {
	return c.context
}

func (c *Connection) InitCodec(codec iface.ICodec) {
	c.Transport = codec
}

// NewConnection 创建连接的方法
func NewConnection(server iface.IServer, conn net.Conn, connID int64, OpCode ws.OpCode) *Connection {
	//初始化Conn属性
	c := &Connection{
		Server:       server,
		Conn:         conn,
		ConnID:       connID,
		isClosed:     false,
		ExitBuffChan: make(chan bool, 1),
		msgBuffChan:  make(chan []byte, maxMsgBuffChan),
		property:     make(map[string]interface{}),
		WsOpCode:     OpCode,
	}

	//将新创建的Conn添加到链接管理中
	c.Server.GetConnMgr().Add(c)
	return c
}

/*
	读消息Goroutine，用于从客户端中读取数据
*/
func (c *Connection) StartReader(wgStart *sync.WaitGroup) {
	fmt.Println("[Reader Goroutine is running]")
	defer fmt.Println(c.RemoteAddr().String(), "[conn Reader exit!]")
	defer c.Stop()

	buffer := make([]byte, 1024*4)
	var currBuffer []byte // need a deep copy of buffer
	wgStart.Done()
	for {
		//解码
		//把解码前内容给消息处理器
		if c.Transport == nil {
			//去找设置解码的方法
			c.Server.ConnCodec(c)
		}
		if c.Server.ServerType() == "websocket" {

			header, err := ws.ReadHeader(c.Conn)
			fmt.Println("StartReader1", header)
			if err != nil {
				// handle error
				fmt.Println("StartReader ReadHeader:", err)
				return
			}
			if header.OpCode == ws.OpText || header.OpCode == ws.OpBinary {
				c.WsOpCode = header.OpCode
			}
			payload := make([]byte, header.Length)
			//这里不是阻塞的吗？

			_, err = io.ReadFull(c.Conn, payload)
			if err != nil {
				// handle error
				fmt.Println("StartReader ReadFull:", err)
				continue
			}
			if header.Masked {
				ws.Cipher(payload, header.Mask, 0)
			}
			currBuffer = append(currBuffer, payload...)
			// Reset the Masked flag, server frames must not be masked as
			// RFC6455 says.
			header.Masked = false
			fmt.Println("StartReader2")
			if header.OpCode == ws.OpClose {
				return
			}
			fmt.Println("StartReader header:", header)

		} else {
			n, err := c.Conn.Read(buffer)
			if err != nil {
				fmt.Println("StartReader [Reader Conn.Read]err:", err)
				return
			}
			fmt.Println("StartReader [Reader currBuffer] n", n)
			currBuffer = append(currBuffer, buffer[:n]...)
		}
		fmt.Println("StartReader3")

		if len(currBuffer) == 0 {
			fmt.Println("StartReader currBuffer len 0")
			continue
		}

		var buf []byte
		var pkgLen int //解密后的长度
		var status int //状态
		for {
			buf, pkgLen, status = c.Transport.Decode(currBuffer)
			fmt.Println("StartReader [Reader pkgLen]", pkgLen)
			fmt.Println("StartReader [Reader status]", status)

			if status == PackageError {
				//关闭这个连接
				c.Stop()
			}
			if status == PackageLess {
				//字节流不够。走这里
				break
			}
			if status == PackageFull {
				currBuffer = currBuffer[pkgLen:]
				if buf != nil {
					//把解码内容给消息处理器
					c.Server.Process(buf, c)
				}
				if len(currBuffer) > 0 {
					continue
				}
				currBuffer = nil
				break
			}
			return
		}

	}
}

/*
	写消息Goroutine， 用户将数据发送给客户端
*/
func (c *Connection) StartWriter(wgStart *sync.WaitGroup) {
	fmt.Println("[Writer Goroutine is running]")
	defer fmt.Println(c.RemoteAddr().String(), "[conn Writer exit!]")
	wgStart.Done()
	fmt.Println("Start ... StartWriter")
	for {
		select {
		case data, ok := <-c.msgBuffChan:
			fmt.Println("wwww", c.WsOpCode, data)
			if ok {
				//有数据要写给客户端
				//先触发操作
				c.Server.PreWrite()
				if c.Server.ServerType() == "websocket" {
					if err := wsutil.WriteServerMessage(c.Conn, c.WsOpCode, data); err != nil {
						glog.Errorf("Send Data error:, ", err, " Conn Writer exit")
						return
					}
				} else {
					if _, err := c.Conn.Write(data); err != nil {
						glog.Errorf("Send Data error:, ", err, " Conn Writer  exit")
						return
					}
				}
			} else {
				glog.Errorf("msgBuffChan is Closed")
				break
			}
		case <-c.ExitBuffChan:
			return
		}
	}
}

// Start 启动连接，让当前连接开始工作
func (c *Connection) Start() {
	wgStart := &sync.WaitGroup{}
	//1 开启用户从客户端读取数据流程的Goroutine
	wgStart.Add(2)
	go c.StartReader(wgStart)
	//2 开启用于写回客户端数据流程的Goroutine
	go c.StartWriter(wgStart)
	//初始化连接
	wgStart.Wait()
	fmt.Println("Start ... OnOpened")
	c.Server.OnEvent().OnOpened(c)
	return
}

// Stop 停止连接，结束当前连接状态M
func (c *Connection) Stop() {
	fmt.Println("Conn Stop()...ConnID = ", c.ConnID)
	//如果当前链接已经关闭
	if c.isClosed == true {
		return
	}
	c.isClosed = true

	action := c.Server.OnClosed(c)
	//触发关闭服务
	if action == iface.Shutdown {
		c.Server.OnShutdown()
	}
	// 关闭socket链接
	err := c.Conn.Close()
	if err != nil {
		return
	}
	//关闭Writer
	c.ExitBuffChan <- true

	//关闭该链接全部管道
	close(c.ExitBuffChan)
	close(c.msgBuffChan)
}

// GetConnection 从当前连接获取原始的socket TCPConn
func (c *Connection) GetConnection() net.Conn {
	return c.Conn
}

// GetConnID 获取当前连接ID
func (c *Connection) GetConnID() int64 {
	return c.ConnID
}

// RemoteAddr 获取远程客户端地址信息
func (c *Connection) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

//直接将Message数据发送数据给远程的TCP客户端

func (c *Connection) SendBuffMsg(Protocol bool, data []byte) error {
	fmt.Println("SendBuffMsg...", c.isClosed)
	if c.isClosed == true {
		return errors.New("connection closed when send buff msg")
	}
	//编码
	var buf []byte
	var err error
	if Protocol {
		buf, err = c.Transport.Encode(data)
		if err != nil {
			return errors.New("Pack error msg ")
		}
		//写回客户端
		c.msgBuffChan <- buf
	} else {
		//写回客户端
		c.msgBuffChan <- data
	}

	return nil
}
